package br.schindler.android.wallpaper.milkyway;

import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.Random;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.GLU;
import br.schindler.android.wallpaper.R;
import br.schindler.games.android.gl.base.GLColor;
import br.schindler.games.android.gl.base.GLDrawable;
import br.schindler.games.android.gl.base.glut.GLUTUtils;

public class SolarSystem implements GLDrawable {

	/**
	 * Milkway time
	 */
	private long time = System.currentTimeMillis();

	/**
	 * Current scale
	 */
	private float scale = 1, rev = 1f, ut = 40, radius = 1f;

	private FloatBuffer stars;

	private int nStars = 600;

	private float x = 270, y = 0;

	private Planet bodies[];

	private int viewMode = 1;

	private Random rand = new Random();

	private CelestialBody cam;

	/**
	 * Default constructor
	 */
	public SolarSystem(Context c) {

		rev = 4f;
		ut = 70;
		radius = 5f;

		try {
			bodies = new Planet[] {
					new Planet(new Sun(14), c, new GLColor(1, 1, 0)),
					new Planet(
							new CelestialBody(ut * 0.307499, radius * 0.3829),
							c, new GLColor(1, 0, 0)),
					new Planet(
							new PlanetVenus(ut * 0.723327, radius * .94531f),
							c, new GLColor(1, 1, .61f)),
					new Planet(new PlanetEarth(ut, radius), c, new GLColor(0,
							0, 1)),
					new Planet(new PlanetMars(ut * 1.524, radius * .532), c,
							new GLColor(1, .2f, .21f)),
					new Planet(new PlanetJupiter(ut * 5.204,
							radius * 11.209 * (.2)), c, new GLColor(1, 0, 0)),

			/*
			 * new Planet(new PlanetJupiter(ut*5.204, radius*11.209), c, new
			 * GLColor(1, 0, 0))
			 */};
			cam = new CelestialBody(bodies[4].getInformation(), radius * 6, 0f);
			cam.P = 10;

			//meteor = new GLModel(c, "asteroid.obj");
		} catch (IOException e) {
			e.printStackTrace();
		}

		/*
		 * x = xc+Rcosφsinθ, y = yc+Rsinφsinθ, z = zc+Rcosθ.
		 */

		this.stars = GLUTUtils.allocateFloatBuffer((nStars * 4) * 3);
		this.stars.position(0);
		float distance = 2f;
		for (int i = 0; i < nStars; i++) {
			double phi = rand.nextDouble() * 2 * Math.PI;
			double theta = rand.nextDouble() * Math.PI;
			double dt = distance + rand.nextDouble();
			float x = (float) (ut * dt * Math.sin(phi) * Math.cos(theta));
			float y = (float) (ut * dt * Math.sin(phi) * Math.sin(theta));
			float z = (float) (ut * dt * Math.cos(phi));
			GLUTUtils.addVertex3f(stars, x, y, z);
		}
		this.stars.position(0);
	}

	/**
	 * Sets current scale
	 * 
	 * @param s
	 */
	public SolarSystem(float s) {
		super();
		this.scale = s;
	}

	public void init(GL10 gl, float w, float h) {
		int[] text = { R.drawable.texture_sun, R.drawable.texture_mercury,
				R.drawable.texture_venus, R.drawable.texture_earth,
				R.drawable.texture_mars, R.drawable.texture_jupiter, };

		Planet.start(gl);

		for (int i = 0; i < bodies.length; i++) {
			bodies[i].setTexture(text[i]);

			if (bodies[i].getSatelite() != null) {
				bodies[i].getSatelite().setTexture(R.drawable.texture_moon);
			}
		}

		Planet.load();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * br.schindler.games.android.gl.base.GLDrawable#drawFrame(javax.microedition
	 * .khronos.opengles.GL10)
	 */
	@Override
	public void drawFrame(GL10 gl) {
		// arrays which stores lighting parameters
		// float q[] = { radius*2, radius*2, radius*10, 1.0f };
		float p[] = { 0.0f, 0.0f, 0.0f, 1.0f };
		// Calculate the curent time in seconds
		float t = (float) (System.currentTimeMillis() - time) / 1000f;

		/**
		 * Sets scale
		 */
		gl.glScalef(scale, scale, scale);

		float[] ps = new float[4];
		float[] ambientlight1 = { .3f, .3f, .3f, 1f };
		float[] diffuselight1 = { 1f, 1f, 1f, 1f };
		float[] specularlight1 = { 0.6f, 0.6f, 0.6f, 1f };
		ps[3] = 1;
		for (int s = 0; s < 6; s++) {
			int signal = (s % 2) == 0 ? 1 : -1;
			ps[0] = ps[1] = ps[2] = 0;
			ps[(s / 2) % 3] = signal
					* (float) bodies[0].getInformation().getRadius() * 2f;
			gl.glEnable(GL10.GL_LIGHT0 + s);
			gl.glLightfv(GL10.GL_LIGHT0 + s, GL10.GL_POSITION, ps, 0);
			gl.glLightfv(GL10.GL_LIGHT0 + s, GL10.GL_AMBIENT, ambientlight1, 0);
			gl.glLightfv(GL10.GL_LIGHT0 + s, GL10.GL_DIFFUSE, diffuselight1, 0);
			gl.glLightfv(GL10.GL_LIGHT0 + s, GL10.GL_SPECULAR, specularlight1,
					0);
		}

		// specifying the color to used to clear the background
		// gl.glClearColor(.0f, .0f, .0f, 1.0f);

		// clearing the color buffer and depth buffer
		// gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		if (viewMode > 1) {
			int[] origem = { 0, 0, 1, 2, 3, 4, 5, 3, 3 };
			int[] dest = { 0, 0, 1, 2, 3, 4, 5, 0, 0 };

			int ori = origem[viewMode];
			int dst = dest[viewMode];

			double[] pm = bodies[dst].getInformation().currentLocation(t / rev);
			double[] pt;

			// pt =
			// bodies[ori].getSatelite().getInformation().currentLocation(t/rev);
			cam.setCenter(bodies[ori].getInformation());
			cam.r = bodies[ori].getInformation().r * 3;
			pt = cam.currentLocation(t / rev);

			// for(int i = 0; i < 3; i++){ pt[i]=(1.4*(pt[i] -pm[i])); }

			GLU.gluLookAt(gl, (float) pt[0], (float) pt[2], (float) pt[1],
					(float) pm[0], (float) pm[2], (float) pm[1], 0, -1, 0);

		} else {

			gl.glTranslatef(0, 0, -ut * 2f);
			gl.glRotatef(x, 0, 1, 0);
			gl.glRotatef(y, 1, 0, 0);
		}

		gl.glColor4f(1f, 1f, 1f, 0.5f);
		gl.glDisable(GL10.GL_LIGHTING);
		gl.glDisable(GL10.GL_DEPTH_TEST);

		// float fogColor[] = {1f, 1f, 1.0f, 1.0f};

		stars.position(0);
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glEnable(GL10.GL_POINT_SMOOTH);
		gl.glEnable(GL10.GL_BLEND);

		// gl.glDepthMask(GL10.GL_FALSE);
		gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);

		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, stars);

		// gl.glEnable(GL10.GL_FOG);
		// gl.glFogf(GL10.GL_FOG_MODE, GL10.GL_LINEAR);
		// gl.glFogfv(GL10.GL_FOG_COLOR, fogColor, 0);
		// gl.glFogf(GL10.GL_FOG_DENSITY, 1f);

		gl.glPointSize(2f);
		gl.glDrawArrays(GL10.GL_POINTS, 0, nStars / 2);
		gl.glPointSize(1f);
		gl.glDrawArrays(GL10.GL_POINTS, nStars / 2, nStars / 2);

		gl.glDisable(GL10.GL_BLEND);
		gl.glDisable(GL10.GL_POINT_SMOOTH);
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		// gl.glDisable(GL10.GL_FOG);

		// enabling the depth test
		gl.glEnable(GL10.GL_DEPTH_TEST);

		// enabling the lighting
		gl.glEnable(GL10.GL_LIGHTING);

		gl.glPushMatrix();
		gl.glMaterialf(GL10.GL_FRONT, GL10.GL_EMISSION, 20f);
		gl.glEnable(GL10.GL_NORMALIZE);

		bodies[0].drawFrame(gl, t);
		gl.glPopMatrix();

		for (int s = 0; s < 6; s++)
			gl.glDisable(GL10.GL_LIGHT0 + s);

		gl.glEnable(GL10.GL_LIGHT0);
		gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, p, 0);
		t /= rev;

		for (int i = 1; i < bodies.length; i++)
			bodies[i].drawFrame(gl, t);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.schindler.games.android.gl.base.GLDrawable#setAngles(float,
	 * float, float)
	 */
	@Override
	public void setAngles(float x, float y, float z) {
		this.x = x;
		this.y = y;
	}

	/**
	 * Enables or not planet's orbit
	 * 
	 * @param show
	 */
	public void showOrbit(boolean show) {
		if (null != bodies)
			for (Planet n : bodies)
				n.setShowOrbit(show);
	}

	/**
	 * 
	 * @param viewMode
	 */
	public void setViewMode(int viewMode) {
		this.viewMode = viewMode;
	}

	/**
	 * 
	 * @param viewMode
	 */
	public void setTimeMode(int timeMode) {
		rev = timeMode / 10f;
	}

	/**
	 * 
	 * @param time
	 */
	public void setTime(long time) {
		this.time = System.currentTimeMillis() - time;
	}

	/**
	 * 
	 * @return
	 */
	public long getTime() {
		return System.currentTimeMillis() - time;
	}
}
